package il.ac.mta.javaweb.ex1.logic.grids;

import il.ac.mta.javaweb.ex1.logic.BattleShip;
import il.ac.mta.javaweb.ex1.logic.ShipSqaure;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;

public class BattleShipsGrid extends GridBase
{
	public static final int SHIP_MIN_LENGTH = 2;
	public static final int SHIP_MAX_LENGTH = 5;

	public static final int MAX_ALLOWED_SHIPS_SIZE_TWO = 1;
	public static final int MAX_ALLOWED_SHIPS_SIZE_THREE = 2;
	public static final int MAX_ALLOWED_SHIPS_SIZE_FOUR = 1;
	public static final int MAX_ALLOWED_SHIPS_SIZE_FIVE = 1;

	private static final int MAX_TOTAL_HITS = 17;
	private int hitCounter = 0;

	private final Hashtable<Integer, Integer> numOfShipsCounter = new Hashtable<Integer, Integer>();
	private final Hashtable<Integer, Integer> maxNumOfShipsOfLength = new Hashtable<Integer, Integer>();

	private final List<BattleShip> battleShips = new ArrayList<BattleShip>();
	private final Hashtable<Point, ShipSqaure> positionsOfShips = new Hashtable<Point, ShipSqaure>();

	public BattleShipsGrid()
	{
		initNumOfShipsCounter();
		initMaxNumOfShipsOfLength();
		initStringGridSquares();
	}

	private void initStringGridSquares()
	{
		for (int i = 0; i < stringGridSquares.length; ++i)
		{
			for (int j = 0; j < stringGridSquares[0].length; ++j)
			{
				stringGridSquares[i][j] = " ";
			}
		}
	}

	public boolean allShipsSunk()
	{
		return hitCounter >= MAX_TOTAL_HITS;
	}

	public boolean bomb(Point position)
	{
		if (getPositionsOfShips().containsKey(position))
		{
			getPositionsOfShips().get(position).setHit(true);
			++hitCounter;
			return true;
		}

		return false;
	}

	public boolean trySetShip(BattleShip ship)
	{
		if (canSetShip(ship))
		{
			battleShips.add(ship);
			
			for (ShipSqaure square : ship.getShipSqaures())
			{
				getPositionsOfShips().put(square.getPosition(), square);
				stringGridSquares[square.getPosition().x][square.getPosition().y] = "O";
			}

			numOfShipsCounter.put(ship.getLength(),
					numOfShipsCounter.get(ship.getLength()) + 1);

			return true;
		}

		return false;
	}

	// Private Methods
	private void initMaxNumOfShipsOfLength()
	{
		maxNumOfShipsOfLength.put(TWO, ONE);
		maxNumOfShipsOfLength.put(THREE, TWO);
		maxNumOfShipsOfLength.put(FOUR, ONE);
		maxNumOfShipsOfLength.put(FIVE, ONE);
	}

	private void initNumOfShipsCounter()
	{
		numOfShipsCounter.put(TWO, ZERO);
		numOfShipsCounter.put(THREE, ZERO);
		numOfShipsCounter.put(FOUR, ZERO);
		numOfShipsCounter.put(FIVE, ZERO);
	}

	private boolean canSetShip(BattleShip ship)
	{
		if (!isShipLengthValid(ship))
		{
			return false;
		}
		else if (exceedsMaximumAllowedShipsOfLength(ship))
		{
			return false;
		}
		else if (!isInGrid(ship))
		{
			return false;
		}
		else if (isOverlappingExistingShip(ship))
		{
			return false;
		}

		return true;
	}

	private boolean exceedsMaximumAllowedShipsOfLength(BattleShip ship)
	{
		return numOfShipsCounter.get(ship.getLength()) >= maxNumOfShipsOfLength
				.get(ship.getLength());
	}

	private boolean isShipLengthValid(BattleShip ship)
	{
		return ship.getLength() >= SHIP_MIN_LENGTH
				&& ship.getLength() <= SHIP_MAX_LENGTH;
	}

	private boolean isOverlappingExistingShip(BattleShip ship)
	{
		for (ShipSqaure newSquare : ship.getShipSqaures())
		{
			for (BattleShip positionedShip : battleShips)
			{
				for (ShipSqaure positionedSquare : positionedShip
						.getShipSqaures())
				{
					if (newSquare.getPosition().x == positionedSquare
							.getPosition().x
							&& newSquare.getPosition().y == positionedSquare
									.getPosition().y)
					{
						return true;
					}
				}
			}
		}

		return false;
	}

	private boolean isInGrid(BattleShip ship)
	{
		boolean isInGrid = true;

		for (ShipSqaure square : ship.getShipSqaures())
		{
			isInGrid &= isInGrid(square.getPosition());
		}

		return isInGrid;
	}

	private boolean isInGrid(Point position)
	{
		return position.x >= ZERO && position.x < WIDTH && position.y >= ZERO
				&& position.y < HEIGHT;
	}

	public Hashtable<Point, ShipSqaure> getPositionsOfShips()
	{
		return positionsOfShips;
	}

	public boolean isHitAtPoint(Point position)
	{
		ShipSqaure square = this.getPositionsOfShips().get(position);
		if (square != null)
		{
			return square.isHit();
		}

		return false;
	}

	@Override
	protected Set<Point> getSetOfPositionsOfShips()
	{
		return this.getPositionsOfShips().keySet();
	}

	@Override
	protected Hashtable<Point, ShipSqaure> getMapOfPositionsOfShips()
	{
		return this.getPositionsOfShips();
	}


	public void reinitGrid()
	{
		hitCounter = 0;

		for (ShipSqaure square : this.getPositionsOfShips().values())
		{
			square.setHit(false);
		}
	}
}
